package main;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class UserInputListener implements Runnable {
	private String username;
	private int port;
	private String myIp;
	private FileUtil fileUtil;
	// person this thread is looking for at the current moment. name only.
	private String desiredName;
	// json record of person for which this Thread was looking for. HTTPRequestHandler sets it.
	private String answerForDesiredName;

	UserInputListener(String name, int port, FileUtil fileUtil) {
		this.username = name;
		this.port = port;
		this.fileUtil = fileUtil;
		InetAddress addr;
		try {
			addr = InetAddress.getLocalHost();
			myIp = addr.getHostAddress();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
	}

	public void run() {
		System.out.println("Checking if username \"" + this.username
				+ "\" is already occupied...");
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		String text = null;

		// while username is wrong keep asking for new:
		while (nameIsOccupied(username)) {
			try {
				System.out
						.println("Username  \""
								+ this.username
								+ "\" is already occupied, you must choose another one!");
				System.out.print("Pick new username: ");
				text = in.readLine();
				username = text;
			} catch (IOException e) {
				e.printStackTrace();
			}
		
		}
		System.out.println("This name is not occupied. You are " + username);

		fileUtil.setMyself("[\"" + this.username + "\",\"" + this.myIp + ":"
				+ this.port + "\"]");

		// name is selected
		// TODO sendname to all

		for (String line : fileUtil.getLines()) {
			String sentRecord = "[\"" + this.username + "\",\"" + this.myIp
					+ ":" + this.port + "\"]";
			sendNameRequest(line, sentRecord);
		}

		while (true) {
			try {
				text = in.readLine();
			} catch (IOException e1) {
				e1.printStackTrace();
			}

			if (text.equals("connect")) {
				// this.sendRequest();
				// this.fetchFile("192.168.0.101:1500");
				askNamesRequest("[\"Bob\",\"192.168.0.100:2000\"]");
			}
			if (text.equals("printIp")) {
				this.printMyIp();
			}

			if (text.startsWith("chatWith ")) {

				// gets username:
				String record = userSearch(text.substring(9));
				// here be
				if (record.equals("Not found")) {
					System.out.println("Dont know where this user is");
				} else {
					System.out.println("Found " + text
							+ ". You can send him a message.");
					while (!text.equals("quit")) {
						try {
							text = in.readLine();
						} catch (IOException e) {
							e.printStackTrace();
						}

						sendMessageRequest(record, text);
					}
				}

			}

			// System.out.println("You have entered: "+text/*+" Result:"+this.checkName(text)*/);
		}

	}

	/**
	 * @param record
	 *            to whom ["name", "ip:port"] (can contain , at the end)
	 * @param message
	 */
	public void sendMessageRequest(String record, String message) {
		String[] temp = this.fileUtil.parseRecord(record);
		String name = temp[0];
		String address = temp[1];
		try {
			message=URLEncoder.encode(message, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		String toUrl = "http://" + address + "/chat/sendmessage?myname="
				+ this.username + "&myip=" + this.myIp + ":" + this.port
				+ "&message=" + message + "&ttl=7";
		String userInfo = name + " " + address;
		sendRequest2(toUrl, userInfo, false);
	}

	/**
	 * @param record
	 *            to whom ["name", "ip:port"] (can contain , at the end)
	 * @param soughtPerson
	 *            username of a person asked machine should check for
	 */
	public void findNameRequest(String record, String soughtPerson) {
		String[] temp = this.fileUtil.parseRecord(record);
		String name = temp[0];
		String address = temp[1];

		String toUrl = "http://" + address + "/chat/findname?name="
				+ soughtPerson + "&myip=" + this.myIp + ":" + this.port
				+ "&ttl=7";
		String userInfo = name + " " + address;
		sendRequest2(toUrl, userInfo, false);
	}

	/**
	 * @param record
	 *            to whom ["name", "ip:port"] (can contain , at the end)
	 * @param sentRecord
	 *            data we are sending ["name", "ip:port"] (can contain , at the
	 *            end)
	 */
	public void sendNameRequest(String record, String sentRecord) {
		String[] temp = this.fileUtil.parseRecord(record);
		String receiverName = temp[0];
		String receiverAddress = temp[1];

		String[] temp2 = this.fileUtil.parseRecord(sentRecord);
		String sentName = temp2[0];
		String sentAddress = temp2[1];

		String toUrl = "http://" + receiverAddress + "/chat/sendname?name="
				+ sentName + "&ip=" + sentAddress + "&ttl=7";
		String userInfo = receiverName + " " + receiverAddress;
		sendRequest2(toUrl, userInfo, false);
	}

	/**
	 * @param record
	 *            who are we asking
	 */
	public void askNamesRequest(String record) {
		String[] temp = this.fileUtil.parseRecord(record);
		String receiverName = temp[0];
		String receiverAddress = temp[1];

		String toUrl = "http://" + receiverAddress + "/chat/asknames"
				+ "?ttl=7";
		String userInfo = receiverName + " " + receiverAddress;
		sendRequest2(toUrl, userInfo, true);
	}

	/**
	 * @param toUrl
	 *            connection url
	 * @param userInfo
	 *            info about user ()
	 * @param responseNeeded
	 */
	private void sendRequest2(String toUrl, String userInfo,
			boolean responseNeeded) {
		HttpURLConnection connection = null;
		try {
//			System.out.println("sending request: " + toUrl);
			URL url = new URL(toUrl);
			connection = (HttpURLConnection) url.openConnection();
			connection.connect();

			// not used, but must be created for http to work properly:
			BufferedReader input = new BufferedReader(new InputStreamReader(
					connection.getInputStream()));
			// System.out.println(input.readLine());
//			System.out.println("request sent");
			if (responseNeeded) {
				getJSONResponse(input);
			}
		} catch (MalformedURLException e1) {
			e1.printStackTrace();
		} catch (IOException e1) {
			// java.net.ConnectException in case of timeout:
			System.out.println("Cannot connect to " + userInfo);
		} finally {
			if (null != connection) {
				connection.disconnect();
			}
		}
	}

	/**
	 * reads json file from input and appends lines to local file
	 * 
	 * @param input
	 *            input gotten from connection
	 * @throws IOException
	 */
	private void getJSONResponse(BufferedReader input) throws IOException {
		String line = "";
//		System.out.println("Preparing to read file...");
		while (true) {
			line = input.readLine();
			if (line == null) {
				break;
			}
//			System.out.println(line);
			if (!line.equals("[") && !line.equals("]")) {
				fileUtil.appendLine(line);
			}
		}
	}
	
	private void printMyIp() {
		try {
			InetAddress addr = InetAddress.getLocalHost();
			System.out.println(addr);

			// Get IP Address
			System.out.println(addr.getHostAddress());

			// Get hostname
			String hostname = addr.getHostName();
			System.out.println(hostname);
		} catch (UnknownHostException e) {
		}
	}

	/**
	 * checks if specified username is already registered.
	 * 
	 * @param name
	 *            username to check
	 * @return true if
	 */
	private boolean nameIsOccupied(String name) {
		String oldMySelf=userSearch(name);
		String newMySelf = "[\"" + this.username + "\",\"" + this.myIp + ":"
				+ this.port + "\"]";
		if (oldMySelf.equals("Not found") || oldMySelf.equals(newMySelf)) {
			return false;
		}
		
		return true;
	}

	/**
	 * @param searchedName
	 *            who we search for
	 * @return Line from json file
	 */
	private synchronized String userSearch(String searchedName) {
		// try to find contact in the local file.
		System.out.println("Checking my file");
		String record = fileUtil.findRecordLine(searchedName);
		if (!record.equals("Not found")) {
			return record;
		} else {
//			setDesiredName(searchedName);
			this.desiredName=searchedName;
			System.out.println("No such contact in my file.");
			// If contact isn't present in the local file, then all non-asked
			// contacts will be asked (all known contacts in the beginning)
			List<String> alreadyAsked = new ArrayList<String>();
			while (true) {
				System.out
						.println("Asking others if they know " + searchedName);
				List<String> lines = fileUtil.getLines();
				lines.removeAll(alreadyAsked); // only new contacts are left in
												// "lines"
				// if there is no one new to ask, then our search has failed:
				if (lines.isEmpty()) {
					System.out.println("Everyone has been asked.");
//					setDesiredName(null);
					this.desiredName=null;
					return "Not found";
				}
				// ask if new contacts know needed contact:
				for (String line : lines) {
					findNameRequest(line, searchedName);
					// actually, separate threads would be better, but meh..
				}
				try {
					System.out.println("Now waiting for an answer.");
					this.wait(2000); // bylo 2000
					if(this.answerForDesiredName!=null){
						
						// Using temp so we can clear answerForDesiredName and desiredName before returning.
						String temp=this.answerForDesiredName;
						this.answerForDesiredName=null;
						this.desiredName=null;
						return temp;
					}
					
					// didn't get an answer => ask their files. new contacts
					// will be asked in the next cycle.
					System.out
							.println("Didn't get an answer. Asking for files");
					for (String line : lines) {
						askNamesRequest(line);// Synchronized request
					}

				} catch (InterruptedException e) {
					// TODO got answer:
					// Nikogda ne zapuskaetsja, tak kak notify() ne vyzyvaet InterruptedException
					// InterruptedException proisxodit tol'ko togda kogda Thread byla prervana "nekrasivo"
					// 
//					setDesiredName(null);
//					return getAnswerForDesiredName();

				}

				// now we have finished asking contacts in "lines":
				alreadyAsked.addAll(lines);

			}// -while
		}
	}

	/**
	 * @return soughtForName OR NULL if UserInputListener is not searching for
	 *         anyone.
	 */
	public synchronized String getDesiredName() {
		return desiredName;
	}

	
	// Zachem voobshe nuzhen PRIVATE!!! getter i setter !?
//	private synchronized void setDesiredName(String name) {
//		desiredName = name;
//	}
	
//	private synchronized String getAnswerForDesiredName() {
//		return answerForDesiredName;
//	}

	public synchronized void setAnswerForDesiredName(String json) {
		answerForDesiredName = json;
	}

}
